package cn.yj.core.business.service;

import cn.yj.annotation.pagehelper.page.Page;
import cn.yj.api.FileUploadHandler;
import cn.yj.cgs.dba.entity.Images;
import cn.yj.cgs.dba.entity.Product;
import cn.yj.cgs.dba.entity.UserViewsAgree;
import cn.yj.cgs.dba.entity.vo.ProductFormVo;
import cn.yj.cgs.dba.entity.vo.ProductVo;
import cn.yj.cgs.dba.mapper.ImagesMapper;
import cn.yj.cgs.dba.mapper.ProductMapper;
import cn.yj.cgs.dba.mapper.UserViewsAgreeMapper;
import cn.yj.common.*;
import cn.yj.common.config.BeanTool;
import cn.yj.commons.StringPools;
import cn.yj.commons.utils.MapUtils;
import cn.yj.commons.utils.StringUtils;
import cn.yj.core.business.service.iservice.IProductService;
import cn.yj.core.kwfilter.KeyWorkFilterService;
import cn.yj.email.EmailType;
import cn.yj.email.handle.DefaultEmailHandler;
import cn.yj.email.handle.Email;
import cn.yj.entity.Base64Entity;
import cn.yj.entity.R;
import cn.yj.params.check.annotation.CheckObjectValue;
import cn.yj.params.check.annotation.KeyValue;
import cn.yj.params.check.annotation.Require;
import cn.yj.tools.exception.ServiceException;
import cn.yj.tools.readconfig.PropertiesUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author 永健
 * @since 2021-06-15 16:16
 */
@Service
public class ProductService extends ServiceImpl<ProductMapper, Product> implements IProductService {

    @Resource
    private ImagesMapper imagesMapper;

    @Resource
    private UserViewsAgreeMapper userViewsAgreeMapper;

    @Resource
    private CustomerService customerService;


    @Override
    public Page<ProductVo> pageList(@Require Map<String, Object> params, @Require Page<ProductVo> page) {
        params.put("deleted", Enums.Common.FALSE_V);
        params.put("auditStatus", Enums.Article.STATUS.SUCCESS.name());

        String typeNos = MapUtils.getStringVal(params, "typeNos");
        params.remove("typeNos");
        if (StringUtils.isNotBlank(typeNos)) {
            params.put("typeNos", Arrays.asList(typeNos.split(",")));
        }
        params.put("status", Enums.Product.STATUS.NORMAL.name());
        baseMapper.pageList(params, page);
        filterHtml(page.getRows());
        return page;
    }


    private void filterHtml(List<ProductVo> rows) {
        rows.forEach(productVo -> {
            String description = productVo.getDescription();
            productVo.setDescription(description.replaceAll("<br/>", " "));
        });
    }

    @Override
    @CheckObjectValue(keyValue = @KeyValue(type = ProductFormVo.class, name = {"userCode", "productName",
            "description", "price", "typeNo", "images", "stock", "schoolName"}))
    public R save(ProductFormVo entity) {

        // 检查是否完善了个人联系方式
        if (!customerService.authenticated(entity.getUserCode())) {
            throw new ServiceException("请先填写联系方式哦~，否则他人无法联系到你");
        }
        entity.setProductNo(UUIdUtils.getCode());
        Product product = new Product();
        BeanUtils.copyProperties(entity, product);
        String msg = filterWord(product);
        handlerImg(entity);

        // 有图片需要人工审核
        if (!entity.getImages().isEmpty()) {
            product.setAuditStatus(Enums.Article.STATUS.DOING.name());
            msg = "物品已保存，审核中";
        }

        product.setCity(customerService.getCustomByCode(entity.getUserCode()).getCity());
        product.setStatus(Enums.Product.STATUS.NORMAL.name());
        String description = entity.getDescription();
        product.setDescription(description.replaceAll("\n", "<br/>"));
        AppExecutor.exec(() -> {
            Email email = new Email(EmailType.审核通知, "文章审核通知");
            email.setRecEmail(email.getFrom());
            AppExecutor.exec(() -> BeanTool.getBean(DefaultEmailHandler.class).sendEmail(email));
        });
        return super.save(product)? R.success(msg) : R.error();
    }

    private String filterWord(Product entity) {
        Set<String> words = KeyWorkFilterService.filter_search_out_word(entity.getProductName(),
                customerService.getOpenIdByUserCode(entity.getUserCode()));
        words.addAll(KeyWorkFilterService.filter_search_out_word(entity.getDescription(),
                customerService.getOpenIdByUserCode(entity.getUserCode())));
        String msg = "物品已发布";
        entity.setAuditStatus(Enums.Article.STATUS.DOING.name());
        if (!words.isEmpty()) {
            msg = "您的文章内容疑似存在敏感词 [ " + StringUtils.join(words, ",") + "],文章需要进行人工核实，方可正常发布! 请同学用语文明 " + "否则账号会被封禁～";
        }
        return msg;
    }


    @Override
    @CheckObjectValue(keyValue = @KeyValue(type = ProductFormVo.class, name = {"id", "userCode", "productName",
            "description", "price", "productNo", "typeNo", "stock", "schoolName"}))
    public R updateById(ProductFormVo entity) {
        Product product = new Product();
        BeanUtils.copyProperties(entity, product);
        String msg = filterWord(product);
        handlerImg(entity);
        // 有图片需要人工审核
        if (!entity.getImages().isEmpty()) {
            product.setAuditStatus(Enums.Article.STATUS.DOING.name());
            msg = "物品已保存，审核中";
        }
        product.setStatus(Enums.Product.STATUS.NORMAL.name());
        String description = entity.getDescription();
        product.setDescription(description.replaceAll("\n", "<br/>"));
        AppExecutor.exec(() -> {
            Email email = new Email(EmailType.审核通知, "文章审核通知");
            email.setRecEmail(email.getFrom());
            AppExecutor.exec(() -> BeanTool.getBean(DefaultEmailHandler.class).sendEmail(email));
        });
        return super.updateById(product)? R.success(msg) : R.error();
    }

    @CheckObjectValue(keyValue = @KeyValue(type = Map.class, name = {"userCode"}))
    @Override
    public Page<ProductVo> getMyProduct(Map<String, Object> params, Page<ProductVo> page) {
        params.put("deleted", Enums.Common.FALSE_V);
        String typeNos = MapUtils.getStringVal(params, "typeNos");
        params.remove("typeNos");
        if (StringUtils.isNotBlank(typeNos)) {
            params.put("typeNos", Arrays.asList(typeNos.split(",")));
        }
        baseMapper.pageList(params, page);
        filterHtml(page.getRows());
        return page;
    }

    /**
     * 将状态修改为已售出
     *
     * @param params - userCode
     *               - productNo
     */
    @CheckObjectValue(keyValue = @KeyValue(type = Map.class, name = {"userCode", "productNo"}))
    @Override
    public boolean updateStatus(Map<String, Object> params) {
        Product product = new Product();
        String productNo = MapUtils.getStringVal(params, "productNo");
        Product p = baseMapper.selectByNoForUpdate(productNo);
        if (!Enums.Article.STATUS.SUCCESS.name().equals(p.getAuditStatus())) {
            throw new ServiceException("物品未发布，无法操作");
        }
        product.setStatus(Enums.Product.STATUS.SOLD.name());
        return super.update(product, new UpdateWrapper<Product>().lambda().eq(Product::getUserCode,
                MapUtils.getStringVal(params, "userCode")).eq(Product::getProductNo, productNo));
    }

    @CheckObjectValue(keyValue = @KeyValue(type = Map.class, name = {"userCode", "productNo"}))
    @Override
    public boolean polish(Map<String, Object> params) {
        Product product = new Product();
        product.setUpdateTime(new Date());
        return super.update(product, new UpdateWrapper<Product>().lambda().eq(Product::getUserCode,
                MapUtils.getStringVal(params, "userCode")).eq(Product::getProductNo, MapUtils.getStringVal(params,
                "productNo")));
    }

    @CheckObjectValue(keyValue = @KeyValue(type = Map.class, name = {"userCode", "productNo"}))
    @Override
    public boolean deleteByNo(Map<String, Object> params) {
        return super.remove(new UpdateWrapper<Product>().lambda().eq(Product::getUserCode,
                MapUtils.getStringVal(params, "userCode")).eq(Product::getProductNo, MapUtils.getStringVal(params,
                "productNo")));
    }


    private static File WATER_FILE;

    static {
        WATER_FILE = new File(PropertiesUtils.getStringValue("school-cbd.water-img-path"));
    }


    private void uploadImg(ImagesMapper imagesMapper, CyclicBarrier cyclicBarrier, String imgBase64, String articleNo) {
        try {
            // 水印处理
            imgBase64 = ImageUtils.addImageWaterMark(imgBase64, WATER_FILE);
        } catch (IOException e) {
            log.error("水印处理异常", e);
        }

        try {
            Base64Entity base64Entity = new Base64Entity(imgBase64,
                    Enums.Minio.Bucket.product.name().concat("/").concat(UUIdUtils.getCode().concat(StringPools.JPG)));
            base64Entity.setContentType(Enums.Minio.ContentType.IMG.getContentType());

            String upload = FileUploadHandler.getInstant().upload(base64Entity);
            Images image = new Images(Enums.Images.TYPE.PRODUCT.name(), upload, articleNo);
            imagesMapper.insert(image);
            cyclicBarrier.await();
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException("图片处理异常，请重试");
        }
    }

    private void handlerImg(ProductFormVo articleFormVo) {
        List<String> images = articleFormVo.getImages();
        if (StringUtils.isNotNull(images) && !images.isEmpty()) {
            if (images.size() > 6) {
                throw new ServiceException("最多只能上传6张图片");
            }

            ExecutorService executorService = Executors.newFixedThreadPool(images.size());
            final CyclicBarrier cyclicBarrier = new CyclicBarrier(images.size() + Enums.Common.ONE);

            // 图片上传
            images.forEach(img -> {
                executorService.execute(() -> uploadImg(imagesMapper, cyclicBarrier, img,
                        articleFormVo.getProductNo()));
            });

            try {
                cyclicBarrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
            executorService.shutdown();
        }
    }


    @Override
    public void viewCountAdd(String userCode, @Require String productNo) {
        if (userViewsAgreeMapper.count(userCode, productNo, Enums.Article.USER_VIEW_TYPE.VIEW.name()) > Enums.Common.ZERO) {
            return;
        }
        Product product = baseMapper.selectByNoForUpdate(productNo);
        super.update(new Product().setViewCount(product.getViewCount() + 1),
                new UpdateWrapper<Product>().lambda().eq(Product::getProductNo, product.getProductNo()));
        if (StringUtils.isNotBlank(userCode)) {
            userViewsAgreeMapper.insert(new UserViewsAgree(userCode, productNo,
                    Enums.Article.USER_VIEW_TYPE.VIEW.name()));
        }
    }

    @Override
    public List<NameValue> getTypesList() {
        return baseMapper.typeList();
    }

    @Override
    public int deleteProductImg(@Require Integer[] imgId) {
        AppExecutor.exec(() -> {
            List<Integer> integers = Arrays.asList(imgId);
            List<Images> images = imagesMapper.selectBatchIds(integers);
            images.forEach(img -> {
                try {
                    FileUploadHandler.getInstant().delete(img.getImgUrl());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        });

        return imagesMapper.deleteBatchIds(Arrays.asList(imgId));
    }

    @Override
    public List<Images> selectProductImg(@Require String productNo) {
        return imagesMapper.selectList(new QueryWrapper<Images>().lambda().eq(Images::getType,
                Enums.Images.TYPE.PRODUCT.name()).eq(Images::getCode, productNo));
    }

    @Override
    public ProductVo selectByNo(@Require String productNo) {
        ProductVo productFormVo = baseMapper.selectByNo(productNo);
        productFormVo.setImageList(this.selectProductImg(productNo));
        return productFormVo;
    }

    @Override
    public ProductVo editByNo(@Require String productNo) {
        ProductVo productFormVo = baseMapper.selectByNo(productNo);
        productFormVo.setImageList(this.selectProductImg(productNo));
        productFormVo.setDescription(productFormVo.getDescription().replaceAll("<br/>", "\n"));
        return productFormVo;
    }

}
